﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Data;
using System.Globalization;

namespace MC34063_Universal_Calculator
{
    public class OutputData : INotifyPropertyChanged
    {
        public enum RegulatorType { DOWN, UP, UP_DOWN, INVERT, NONE };
        private enum Optimization { LOWER, BEST, UPPER };

        private class VoltageDivider
        {
            double _r1;

            public double R1
            {
                get { return _r1; }
                set { _r1 = value; }
            }
            double _r2;

            public double R2
            {
                get { return _r2; }
                set { _r2 = value; }
            }
            double _Vdivider;

            public double Vdivider
            {
                get { return _Vdivider; }
                set { _Vdivider = value; }
            }

            double _expectedRatio;

            public double ExpectedRatio
            {
                get { return _expectedRatio; }
                set { _expectedRatio = value; }
            }

            double _expectedCurrent;

            public double ExpectedCurrent
            {
                get { return _expectedCurrent; }
                set { _expectedCurrent = value; }
            }

            public double ErrorRatio
            {
                get
                {
                    return Math.Abs(_r1 / (_r1 + _r2) - _expectedRatio);
                }
            }

            public double ErrorCurrent
            {
                get
                {
                    if (_Vdivider / (_r1 + _r2) < 100E-6)
                        return 10000;
                    else
                        return Math.Abs(_Vdivider / (_r1 + _r2) - _expectedCurrent);
                }
            }

            public double Error
            {
                get
                {
                    return ErrorRatio + ErrorCurrent;
                }
            }

            public double Current
            {
                get
                {
                    return _Vdivider / (_r1 + _r2);
                }
            }

            public double Vout
            {
                get
                {
                    return _Vdivider * _r1 / (_r1 + _r2);
                }
            }

            public VoltageDivider()
            {
            }

            public VoltageDivider(double r1, double r2, double vDivider, double expectedRatio, double expacedCurrent)
            {
                _r1 = r1;
                _r2 = r2;
                _expectedRatio = expectedRatio;
                _expectedCurrent = expacedCurrent;
                _Vdivider = vDivider;
            }

            public double CalculateRealVout(double vRef)
            {
                return (vRef * (R1 + R2)) / R1;
            }
        }

        InputData _inputData;

        int _frequency;

        public int Frequency
        {
            get { return _frequency; }
            set { _frequency = value; NotifyPropertyChanged("Frequency"); }
        }
        
        double _dutycycle;

        public double DutyCycle
        {
            get { return _dutycycle; }
            set { _dutycycle = value; NotifyPropertyChanged("DutyCycle"); }
        }

        double _Iinmax;

        public double Iinmax
        {
            get { return _Iinmax; }
            set { _Iinmax = value; NotifyPropertyChanged("Iinmax"); }
        }
        double _Ioutmax;

        public double Ioutmax
        {
            get { return _Ioutmax; }
            set { _Ioutmax = value; NotifyPropertyChanged("Ioutmax"); }
        }
        double _Vout;

        public double Vout
        {
            get { return _Vout; }
            set { _Vout = value; NotifyPropertyChanged("Vout"); }
        }
        double _Vripple;

        public double Vripple
        {
            get { return _Vripple; }
            set { _Vripple = value; NotifyPropertyChanged("Vripple"); }
        }
        int _ct;

        public int Ct
        {
            get { return _ct; }
            set { _ct = value; NotifyPropertyChanged("Ct"); }
        }
        double _rdriver;

        public double Rdriver
        {
            get { return _rdriver; }
            set { _rdriver = value; NotifyPropertyChanged("Rdriver"); }
        }
       
        double _rsc;

        public double Rsc
        {
            get { return _rsc; }
            set { _rsc = value; NotifyPropertyChanged("Rsc"); }
        }
        double _L;

        public double L
        {
            get { return _L; }
            set { _L = value; NotifyPropertyChanged("L"); }
        }
        double _Ipeak;

        public double Ipeak
        {
            get { return _Ipeak; }
            set { _Ipeak = value; NotifyPropertyChanged("Ipeak"); }
        }
        double _Co;

        public double Co
        {
            get { return _Co; }
            set 
            {
                _Co = value;
                NotifyPropertyChanged("Co");
                MaxESR = -1 * ((1.5E-3 * Vout) / _vref - _vripplepp + (_ioutmax* _ton) / (value * 1E-6)) / Ipeak;
            }
        }
        double _R1;

        public double R1
        {
            get { return _R1; }
            set { _R1 = value; NotifyPropertyChanged("R1"); }
        }
        double _R2;

        public double R2
        {
            get { return _R2; }
            set { _R2 = value; NotifyPropertyChanged("R2"); }
        }
        double _rB;

        public double RB
        {
            get { return _rB; }
            set { _rB = value; NotifyPropertyChanged("RB"); }
        }
        double _rBE;

        public double RBE
        {
            get { return _rBE; }
            set { _rBE = value; NotifyPropertyChanged("RBE"); }
        }
        double _iDiv;

        public double IDiv
        {
            get { return _iDiv; }
            set { _iDiv = value; NotifyPropertyChanged("IDiv"); }
        }
        double _maxESR;

        public double MaxESR
        {
            get { return _maxESR; }
            set { _maxESR = value; NotifyPropertyChanged("MaxESR"); }
        }
        bool _isEnabledCo = false;

        public bool IsEnabledCo
        {
            get { return _isEnabledCo; }
            set { _isEnabledCo = value; NotifyPropertyChanged("IsEnabledCo"); }
        }

        double _vref;
        double _ioutmax;
        double _vripplepp;
        double _ton;

        List<double> _resistorStockValues;
        List<double> _capacitorStockValues;

        public OutputData()
        {

        }

        public OutputData(ref InputData inputData)
            : this()
        {
            _inputData = inputData;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        public RegulatorType Calculate(InputData inputData, bool useStock)
        {
            RegulatorType regType = RegulatorType.UP;

            string nameLocalized;
            WPFLocalizeExtension.Extensions.LocTextExtension locTextExt = new WPFLocalizeExtension.Extensions.LocTextExtension("MC34063_Universal_Calculator:Global:Andere");
            locTextExt.ResolveLocalizedValue(out nameLocalized);

            if(_resistorStockValues != null)
                _resistorStockValues.Clear();
            _resistorStockValues = null;
            if(_capacitorStockValues != null)
                _capacitorStockValues.Clear();
            _capacitorStockValues = null;

            if (inputData.SelectedRtolValue.Name != nameLocalized)
            {
                _resistorStockValues = new List<double>(inputData.SelectedRtolValue.PartValues);
                _resistorStockValues.Add(10);
            }

            if (inputData.SelectedCtolValue.Name != nameLocalized)
            {
                _capacitorStockValues = new List<double>(inputData.SelectedCtolValue.PartValues);
                _capacitorStockValues.Add(10);
            }

            if (((inputData.Vinmin > 0) && (inputData.Vout < 0)) || ((inputData.Vinmin < 0) && (inputData.Vout > 0)))
            {
                CalculateINVERT(inputData, useStock);
                regType = RegulatorType.INVERT;
            }
            else if ((inputData.Vout > inputData.Vinmin) && (inputData.Vout < inputData.Vinmax))
            {
                CalculateUPDOWN(inputData, useStock);
                regType = RegulatorType.UP_DOWN;
            }
            else if (inputData.Vinmin < inputData.Vout)
            {
                CalculateUP(inputData, useStock);
                regType = RegulatorType.UP;
            }
            else if (inputData.Vinmin > inputData.Vout)
            {
                CalculateDOWN(inputData, useStock);
                regType = RegulatorType.DOWN;
            }
            
            else
            {
                throw new InvalidEnumArgumentException("NONE", -1, typeof(RegulatorType));
            }
            IsEnabledCo = true;
            return regType;
        }

        private VoltageDivider OptimizeDivider(double vRef, double vOut, double iDiv)
        {
            List<VoltageDivider> dividerList = new List<VoltageDivider>();
            double vRatio = vRef / vOut;

            for (int i = 0; i <= 6; i++)
            {
                for (int a = 0; a <= 6; a++)
                {
                    foreach (double r1S in _resistorStockValues)
                    {
                        foreach (double r2S in _resistorStockValues)
                        {
                            dividerList.Add(new VoltageDivider(r1S * Math.Pow(10, i), r2S * Math.Pow(10, a), vOut, vRatio, iDiv * 1.0E-6));
                        }
                    }
                }
            }

            return (from d in dividerList
                    orderby d.Error ascending
                    select d).First();
        }

        private double OptimizeCapacitor(double cVal, Optimization opt)
        {
            List<double> CapacitorList = new List<double>();

            for (int i = -12; i < 0; i++)
            {
                foreach (double cS in _capacitorStockValues)
                {
                    CapacitorList.Add(cS * Math.Pow(10, i));
                }
            }

            switch (opt)
            {
                case Optimization.LOWER:
                    return (from c in CapacitorList
                            where c < cVal
                            orderby c descending
                            select c).First();

                case Optimization.BEST:
                    return (from c in CapacitorList
                            orderby Math.Abs(cVal - c) ascending
                            select c).First();

                case Optimization.UPPER:
                    return (from c in CapacitorList
                            where c > cVal
                            orderby c ascending
                            select c).First();
            }
            return -1;
        }

        private double OptimizeResistor(double rVal, Optimization opt)
        {
            List<double> ResistorList = new List<double>();

            for (int i = -1; i < 6; i++)
            {
                foreach (double rS in _resistorStockValues)
                {
                    ResistorList.Add(rS * Math.Pow(10, i));
                }
            }

            try
            {
                switch (opt)
                {
                    case Optimization.LOWER:
                        return (from c in ResistorList
                                where c < rVal
                                orderby c descending
                                select c).First();

                    case Optimization.BEST:
                        return (from c in ResistorList
                                orderby Math.Abs(rVal - c) ascending
                                select c).First();

                    case Optimization.UPPER:
                        return (from c in ResistorList
                                where c > rVal
                                orderby c ascending
                                select c).First();
                }
            }
            catch (Exception)
            {
                try
                {
                    return (from c in ResistorList
                            where c > rVal
                            orderby c ascending
                            select c).First();
                }
                catch (Exception)
                {
                    return 0;
                }
            }
            return -1;
        }

        private void CalculateUP(InputData inputData, bool useStock)
        {
            // Calcluate R1 and R2 (Reference Voltage Divider)
            R1 = inputData.Vref / (inputData.IDiv * 1.0E-6) * 1E-3;
            R2 = R1 * 1E3 * (inputData.Vout / inputData.Vref - 1.0) * 1E-3;
            Vout = inputData.Vout;

            if (useStock)
            {
                //Get Closest R1 and R2 Match
                VoltageDivider bestDivider = OptimizeDivider(inputData.Vref, inputData.Vout, inputData.IDiv);
                R1 = bestDivider.R1 * 1E-3;
                R2 = bestDivider.R2 * 1E-3;
                Vout = bestDivider.CalculateRealVout(inputData.Vref);
                IDiv = Convert.ToInt32(bestDivider.Current * 1E6);
            }

            // Calculate DutyCycle and Ct
            double dcmax = (double)(Vout + inputData.DiodeVf - inputData.Vinmax) / (double)(inputData.Vinmax - inputData.Vsat);
            double tonptoff = 1 / (double)inputData.Frequency;
            double toff = tonptoff / (dcmax + 1);
            double tonmax = tonptoff - toff;
            double dc = (double)(Vout + inputData.DiodeVf - inputData.Vinmin) / (double)(inputData.Vinmin - inputData.Vsat);
            tonptoff = 1/(double)inputData.Frequency;
            toff = tonptoff / (dc + 1);
            double ton = tonptoff - toff;
            double ct = 4E-5 * ton;

            if (useStock)
            {
                ct = OptimizeCapacitor(ct, Optimization.BEST);
                double frequency = 4E-5 / ct * (1 - 1 / (dc + 1));
                tonptoff = 1 / frequency;
                toff = tonptoff / (dc + 1);
                ton = tonptoff - toff;
            }

            // Calculate Currents, current limiting resistors and Inductivities
            Ipeak = 2 * inputData.Ioutmax * (dc + 1);
            double l = ton * (inputData.Vinmin - inputData.Vsat) / Ipeak;
            double imaxpeak = tonmax * (inputData.Vinmax - inputData.Vsat) / l;
            Iinmax = imaxpeak;
            Rsc = 0.3 / imaxpeak;
            if(useStock)
                Rsc = OptimizeResistor(Rsc, Optimization.LOWER);
            Ct = (int)Math.Floor(1E12 * ct);
            L = 1E6 * l;

            // Calculate output capacitor
            double co = 9 * inputData.Ioutmax * ton / inputData.Vripplepp;
            if (useStock)
            {
                co = OptimizeCapacitor(co, Optimization.UPPER);
            }

            Frequency = (int)Math.Round(1/(ton+toff));
            DutyCycle = 100 * ton * (1/(ton+toff));
            _vref = inputData.Vref;
            _ioutmax = inputData.Ioutmax;
            _vripplepp = inputData.Vripplepp;
            _ton = ton;
            Vripple = inputData.Vripplepp;

            double rDriver = (inputData.Vinmin - inputData.Vsat - Ipeak * Rsc) / (Ipeak / 20 + 0.7 / 100);
            if (useStock)
            {
                rDriver = OptimizeResistor(rDriver, Optimization.BEST);
            }
            Rdriver = rDriver;

            Co = Math.Round(co * 1E6);
        }

        private void CalculateDOWN(InputData inputData, bool useStock)
        {
            // Calcluate R1 and R2 (Reference Voltage Divider)
            R1 = inputData.Vref / (inputData.IDiv * 1.0E-6) * 1E-3;
            R2 = R1 * 1E3 * (inputData.Vout / inputData.Vref - 1.0) * 1E-3;
            Vout = inputData.Vout;

            if (useStock)
            {
                //Get Closest R1 and R2 Match
                VoltageDivider bestDivider = OptimizeDivider(inputData.Vref, inputData.Vout, inputData.IDiv);
                R1 = bestDivider.R1 * 1E-3;
                R2 = bestDivider.R2 * 1E-3;
                Vout = bestDivider.CalculateRealVout(inputData.Vref);
                IDiv = Convert.ToInt32(bestDivider.Current * 1E6);
            }

            // Calculate DutyCycle and Ct

            double dcmax = (double)(Vout + inputData.DiodeVf) / (double)(inputData.Vinmax - inputData.Vsat - Vout);
            double tonptoff = 1 / (double)inputData.Frequency;
            double toff = tonptoff / (dcmax + 1);
            double tonmax = tonptoff - toff;

            double dc = (double)(Vout + inputData.DiodeVf) / (double)(inputData.Vinmin - inputData.Vsat - Vout);
            tonptoff = 1 / (double)inputData.Frequency;
            toff = tonptoff / (dc + 1);
            double ton = tonptoff - toff;
            double ct = 4E-5 * ton;
            

            if (useStock)
            {
                ct = OptimizeCapacitor(ct, Optimization.BEST);
                double frequency = 4E-5 / ct * (1 - 1 / (dc + 1));
                tonptoff = 1 / frequency;
                toff = tonptoff / (dc + 1);
                ton = tonptoff - toff;
            }

            // Calculate Currents, current limiting resistors and Inductivities
            Ipeak = 2 * inputData.Ioutmax;
            Ct = (int)Math.Floor(1E12 * ct);
            L = 1E6 * ton * (inputData.Vinmin - inputData.Vsat - Vout) / Ipeak;
            Iinmax = tonmax * (inputData.Vinmax - inputData.Vsat - Vout) / (L * 1E-6);
            Rsc = 0.33 / Iinmax;
            if(useStock)
                Rsc = OptimizeResistor(Rsc, Optimization.LOWER);

            // Calculate output capacitor
            double co = Ipeak * (ton + toff) / (8 * inputData.Vripplepp);
            if (useStock)
            {
                co = OptimizeCapacitor(co, Optimization.UPPER);
            }

            Frequency = (int)Math.Round(1 / (ton + toff));
            DutyCycle = 100 * ton * (1 / (ton + toff));
            _vref = inputData.Vref;
            _ioutmax = inputData.Ioutmax;
            _vripplepp = inputData.Vripplepp;
            _ton = ton;
            Vripple = inputData.Vripplepp;
            Co = Math.Round(co * 1E6);
        }

        private void CalculateUPDOWN(InputData inputData, bool useStock)
        {
            // Calcluate R1 and R2 (Reference Voltage Divider)
            R1 = inputData.Vref / (inputData.IDiv * 1.0E-6) * 1E-3;
            R2 = R1 * 1E3 * (inputData.Vout / inputData.Vref - 1.0) * 1E-3;
            Vout = inputData.Vout;

            if (useStock)
            {
                //Get Closest R1 and R2 Match
                VoltageDivider bestDivider = OptimizeDivider(inputData.Vref, inputData.Vout, inputData.IDiv);
                R1 = bestDivider.R1 * 1E-3;
                R2 = bestDivider.R2 * 1E-3;
                Vout = bestDivider.CalculateRealVout(inputData.Vref);
                IDiv = Convert.ToInt32(bestDivider.Current * 1E6);
            }

            // Calculate DutyCycle and Ct
            double dcmax = (double)(Vout + inputData.DiodeVf + inputData.Diode2Vf) / (double)(inputData.Vinmax - inputData.Vsat - inputData.Vsat2);
            double tonptoff = 1 / (double)inputData.Frequency;
            double toff = tonptoff / (dcmax + 1);
            double tonmax = tonptoff - toff;
            double dc = (double)(Vout + inputData.DiodeVf + inputData.Diode2Vf) / (double)(inputData.Vinmin - inputData.Vsat - inputData.Vsat2);
            tonptoff = 1 / (double)inputData.Frequency;
            toff = tonptoff / (dc + 1);
            double ton = tonptoff - toff;
            double ct = 4E-5 * ton;

            if (useStock)
            {
                ct = OptimizeCapacitor(ct, Optimization.BEST);
                double frequency = 4E-5 / ct * (1 - 1 / (dc + 1));
                tonptoff = 1 / frequency;
                toff = tonptoff / (dc + 1);
                ton = tonptoff - toff;
            }

            // Calculate Currents, current limiting resistors and Inductivities
            Ipeak = 2 * inputData.Ioutmax * (dc + 1);
            double l = ton * (inputData.Vinmin - inputData.Vsat - inputData.Vsat2) / Ipeak;
            double imaxpeak = tonmax * (inputData.Vinmax - inputData.Vsat - inputData.Vsat2) / l;
            Iinmax = imaxpeak;
            Rsc = 0.3 / imaxpeak;
            if (useStock)
                Rsc = OptimizeResistor(Rsc, Optimization.LOWER);
            Ct = (int)Math.Floor(1E12 * ct);
            L = 1E6 * l;

            // Calculate output capacitor
            double co = 9 * inputData.Ioutmax * ton / inputData.Vripplepp;
            if (useStock)
            {
                co = OptimizeCapacitor(co, Optimization.UPPER);
            }

            Frequency = (int)Math.Round(1 / (ton + toff));
            DutyCycle = 100 * ton * (1 / (ton + toff));
            _vref = inputData.Vref;
            _ioutmax = inputData.Ioutmax;
            _vripplepp = inputData.Vripplepp;
            _ton = ton;
            Vripple = inputData.Vripplepp;

            double ib = Ipeak / 20;
            RBE = 10 * 20 / Ipeak;
            if (useStock)
            {
                RBE = OptimizeResistor(RBE, Optimization.BEST);
            }

            double irbe = inputData.Vsat2 / RBE;
            RB = (inputData.Vinmin - inputData.Vsat - Ipeak * Rsc - inputData.Vsat2) / (ib + irbe);

            if (useStock)
            {
                RB = OptimizeResistor(RB, Optimization.BEST);
            }

            Co = Math.Round(co * 1E6);
        }

        private void CalculateINVERT(InputData inputData, bool useStock)
        {
            // Calcluate R1 and R2 (Reference Voltage Divider)
            R2 = inputData.Vref / (inputData.IDiv * 1.0E-6) * 1E-3;
            R1 = R2 * 1E3 * (Math.Abs(inputData.Vout) / inputData.Vref - 1.0) * 1E-3;
            Vout = inputData.Vout;

            if (useStock)
            {
                //Get Closest R1 and R2 Match
                VoltageDivider bestDivider = OptimizeDivider(inputData.Vref, Math.Abs(inputData.Vout), inputData.IDiv);
                R2 = bestDivider.R1 * 1E-3;
                R1 = bestDivider.R2 * 1E-3;
                Vout = Math.Abs(inputData.Vout)/inputData.Vout * bestDivider.CalculateRealVout(inputData.Vref);
                IDiv = Convert.ToInt32(bestDivider.Current * 1E6);
            }

            // Calculate DutyCycle and Ct
            double dcmax = (double)(Math.Abs(Vout) + inputData.DiodeVf) / (double)(inputData.Vinmax - inputData.Vsat);
            double tonptoff = 1 / (double)inputData.Frequency;
            double toff = tonptoff / (dcmax + 1);
            double tonmax = tonptoff - toff;
            double dc = (double)(Math.Abs(Vout) + inputData.DiodeVf) / (double)(inputData.Vinmin - inputData.Vsat);
            tonptoff = 1 / (double)inputData.Frequency;
            toff = tonptoff / (dc + 1);
            double ton = tonptoff - toff;
            double ct = 4E-5 * ton;

            if (useStock)
            {
                ct = OptimizeCapacitor(ct, Optimization.BEST);
                double frequency = 4E-5 / ct * (1 - 1 / (dc + 1));
                tonptoff = 1 / frequency;
                toff = tonptoff / (dc + 1);
                ton = tonptoff - toff;
            }

            // Calculate Currents, current limiting resistors and Inductivities
            Ipeak = 2 * inputData.Ioutmax * (dc + 1);
            double l = ton * (inputData.Vinmin - inputData.Vsat) / Ipeak;
            double imaxpeak = tonmax * (inputData.Vinmax - inputData.Vsat) / l;
            Iinmax = ton * (inputData.Vinmax - inputData.Vsat) / l;
            Rsc = 0.3 / imaxpeak;
            if (useStock)
                Rsc = OptimizeResistor(Rsc, Optimization.LOWER);
            Ct = (int)Math.Floor(1E12 * ct);
            L = 1E6 * l;

            // Calculate output capacitor
            double co = 9 * inputData.Ioutmax * ton / inputData.Vripplepp;
            if (useStock)
            {
                co = OptimizeCapacitor(co, Optimization.UPPER);
            }

            Frequency = (int)Math.Round(1 / (ton + toff));
            DutyCycle = 100 * ton * (1 / (ton + toff));
            _vref = inputData.Vref;
            _ioutmax = inputData.Ioutmax;
            _vripplepp = inputData.Vripplepp;
            _ton = ton;
            Vripple = inputData.Vripplepp;

            Co = Math.Round(co * 1E6);
        }
    }
}

//(c) 2011 - Tobias Eiseler

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>